package com.fcml.rbmq.study.user.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;


@Slf4j
@Configuration
public class RabbitTemplateConfig {
    @Autowired
    private ConnectionFactory connectionFactory;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        //template.setMandatory(true);

        /**
         * 当消息发送到交换机（exchange）时，回调方法会被调用.
         */
        template.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                redisTemplate.opsForHash().delete("message", correlationData.getId());
                redisTemplate.opsForHash().delete("exchange", correlationData.getId());
                redisTemplate.opsForHash().delete("routingKey", correlationData.getId());
            } else {
                log.error("消息发送失败, cause:{}", cause);
                Message message = (Message) redisTemplate.opsForHash().get("message", correlationData.getId());
                String exchange = (String) redisTemplate.opsForHash().get("exchange", correlationData.getId());
                String routingKey = (String) redisTemplate.opsForHash().get("routingKey", correlationData.getId());
                rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);

                //TODO 会不会由于某些原因，ack一直是false，导致消息不断地被重发。这样需要加发送次数的限制
            }
        });

        /**
         * 当消息从交换机到队列失败时，回调方法被调用。（若成功，则不调用）
         * 需要注意的是：该方法调用后，MsgSendConfirmCallBack中的confirm方法也会被调用，且ack = true
         */
        template.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            log.error("message send to queue failed.");
            log.error("exchange: {}, routingKey: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message);
            rabbitTemplate.convertAndSend(exchange, routingKey, message);

            //TODO 重发次数应该加限制，以免路由错误或者队列不存在
        });

        return template;
    }
}
